package utils.graph_utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;

import components.AbstractNeuron;

public class AStarSearch implements Comparator<Node> {

	public enum Metric {
		DISTANCE, DELAY, STRENGTH, BINARY;
	}
	
	private LinkedList<Node> openSet;
	private HashSet<Node> closedSet;
	private Node start;
	private Node end;
	private double finalPathLength;
	private LinkedList<AbstractNeuron> path;
	
	public AStarSearch(Node start, Node end) {
		this.end = end;
		this.start = start;
		openSet = new LinkedList<Node>();
		openSet.add(start);
		closedSet = new HashSet<Node>();	
		path = new LinkedList<AbstractNeuron>();
	}
	
	public ArrayList<AbstractNeuron> runSearch() {
		Node endNode = null;
		while(!openSet.isEmpty()) {
			ArrayList<Node> frontier = openSet.peek().expand(closedSet);
			closedSet.add(openSet.poll());
			for(Node n : frontier) {
				if(closedSet.contains(n) || openSet.contains(n)) {
					continue;
				}
				if(n == end) {
					endNode = n;
					finalPathLength = n.getDistanceTraveled();
					break;
				}
				n.calcHeuristicEst(end.getN());
				openSet.add(n);


			}
			Collections.sort(openSet, this);
			if(endNode != null) {
				break;
			}
		}
		
		if(endNode == null) {
			return null;
		} else {
			traceRoute(endNode);
			closedSet.addAll(openSet);			
			return new ArrayList<AbstractNeuron>(path);
		}		
		
	}
	
	public void traceRoute(Node n) {
		path.push(n.getN());
		try{
			Node cf = n.getCameFrom();
			while(cf != start) {
				cf = cf.getCameFrom();
				path.push(cf.getN());
			}
		}catch (Exception e) {
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	
	@Override
	public int compare(Node o1, Node o2) {	
		if(o1.getHeuristicEst() < o2.getHeuristicEst()) 
			return -1;
		 else if (o1.getHeuristicEst() > o2.getHeuristicEst()) 
			return 1;
		 else 
			return 0;	
	}

	public double getFinalPathLength() {
		return finalPathLength;
	}

	public LinkedList<AbstractNeuron> getPath() {
		return path;
	}

	
//	public static void main(String [] args) {
//		LIFNeuron rootN = new LIFNeuron(50, true, 1);
//		
//		
//	}
	
}
